/***************************************************************************
    File                 : AbstractFilter.h
    Project              : AlphaPlot
    --------------------------------------------------------------------
    Copyright            : (C) 2007-2009 by Knut Franke, Tilman Benkert
    Email (use @ for *)  : knut.franke*gmx.de, thzs*gmx.net
    Description          : Base class for all analysis operations.

 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *  This program is free software; you can redistribute it and/or modify   *
 *  it under the terms of the GNU General Public License as published by   *
 *  the Free Software Foundation; either version 2 of the License, or      *
 *  (at your option) any later version.                                    *
 *                                                                         *
 *  This program is distributed in the hope that it will be useful,        *
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of         *
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          *
 *  GNU General Public License for more details.                           *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the Free Software           *
 *   Foundation, Inc., 51 Franklin Street, Fifth Floor,                    *
 *   Boston, MA  02110-1301  USA                                           *
 *                                                                         *
 ***************************************************************************/
#ifndef ABSTRACT_FILTER_H
#define ABSTRACT_FILTER_H

#include "AbstractAspect.h"
#include "AbstractColumn.h"
#include <QVector>

/**
 * \brief Base class for all analysis operations.
 *
 * AbstractFilter provides an abstraction for analysis operations. It is
 * modelled on an
 * electronic filtering circuit: From the outside, a filter appears as a black
 * box with
 * a number of input and output ports (obviously, those numbers do not
 * necessarily agree).
 *
 * \section using Using AbstractFilter
 * You can connect one AbstractColumn to each input port using
 * input(int port, AbstractColumn* source). Every output(int port) is realized
 * again by an AbstractColumn, which you can connect to as many other filters,
 * tables
 * or plots as you like.
 * Ownership of the data sources always stays with the class which is providing
 * the data,
 * that is, neither input() nor output() transfer ownership.
 *
 * Furthermore, you can use inputCount() and outputCount() to query the number
 * of
 * input and output ports, respectively and you can obtain label strings for
 * inputs (via
 * inputLabel()) and outputs (via AbstractColumn::label()). This allows generic
 * filter
 * handling routines to be written, which is important for using filters
 * provided by plugins.
 *
 * Its simplicity of use notwithstanding, AbstractFilter provides a powerful and
 * versatile
 * basis also for analysis operations that would not commonly be referred to as
 * "filter".
 * An example of such a more advanced filter implementation is StatisticsFilter.
 *
 * \section subclassing Subclassing AbstractFilter
 * The main design goal was to make implementing new filters as easy as
 * possible.
 * Filters with only one output port can subclass AbstractSimpleFilter, which is
 * even easier
 * to use. Filters with more than one output port have to subclass
 * AbstractFilter directly, which is slightly more involved, because in
 * addition to data transfer between these classes the signals defined by
 * AbstractColumn
 * have to be handled on both inputs and outputs. Signals from data sources
 * connected to the input
 * ports are automatically connected to a matching set of virtual methods, which
 * can be
 * reimplemented by subclasses to handle these events.
 *
 * While AbstractFilter handles the tedious part of connecting a data source to
 * an input port,
 * its subclasses are given a chance to reject such connections (e.g., based on
 * the data type
 * of the source) by reimplementing inputAcceptable().
 *
 * \sa AbstractSimpleFilter
 */
class AbstractFilter : public AbstractAspect {
  Q_OBJECT

 public:
  //! Standard constructor.
  AbstractFilter(const QString& name) : AbstractAspect(name) {}
  //! Destructor.
  virtual ~AbstractFilter() {}

  //! Return the number of input ports supported by the filter or -1 if any
  //! number of inputs is acceptable.
  virtual int inputCount() const = 0;
  /**
   * \brief Return the number of output ports provided by the filter.
   *
   * %Note that this number need not be static, but can be dynamically
   * determined, for example
   * based on the inputs provided to the filter.
   */
  virtual int outputCount() const = 0;
  /**
   * \brief Return the index of the highest input port that is connected.
   *
   * Note that this is different from both the number of ports that
   * <em>could</em> be connected,
   * inputCount(), and the number of ports that actually <em>have been</em>
   * connected, which are
   * not necessarily sequential. In conjunction with input(int), this method can
   * be used to
   * traverse the connected inputs.
   */
  int highestConnectedInput() const { return d_inputs.count() - 1; }
  /**
   * \brief Connect the provided data source to the specified input port.
   * \param port the port number to which to connect
   * \param source the data source to connect to the input port
   * \returns true if the connection was accepted, false otherwise.
   *
   * The port number is checked for validity against inputCount() and both port
   * number and data
   * source are passed to inputAcceptable() for review. If both checks
   * succeed,the
   * source is recorded in #d_inputs.
   * If applicable, the previously connected data source is disconnected before
   * replacing it.
   *
   * You can also use this method to disconnect an input without replacing it
   * with a new one by
   * calling it with source=0.
   *
   * \sa inputAcceptable(), #d_inputs
   */
  bool input(int port, const AbstractColumn* source);
  /**
   * \brief Connect all outputs of the provided filter to the corresponding
   * inputs of this filter.
   * \returns true if all connections were accepted, false otherwise
   *
   * Overloaded method provided for convenience.
   */
  bool input(const AbstractFilter* sources);
  //! Return the input currently connected to the specified port, or 0.
  const AbstractColumn* input(int port) const { return d_inputs.value(port); }
  /**
   * \brief Return the label associated to the given input port.
   *
   * Default labels are In1, In2, ... (or translated equivalents), but
   * implementations can
   * reimplement this method to produce more meaningful labels.
   *
   * Output ports are implicitly labeled through AbstractAspect::name().
   */
  virtual QString inputLabel(int port) const;
  /**
   * \brief Get the data source associated with the specified output port.
   *
   * The returned pointer may be 0 even for valid port numbers, for example if
   * not all required
   * input ports have been connected.
   */
  virtual AbstractColumn* output(int port = 0) = 0;
  //! Overloaded method for const access.
  virtual const AbstractColumn* output(int port = 0) const = 0;

  //! Return the input port to which the column is connected or -1 if it's not
  //! connected
  int portIndexOf(const AbstractColumn* column) {
    for (int i = 0; i < d_inputs.size(); i++)
      if (d_inputs.at(i) == column) return i;
    return -1;
  }

 protected:
  /**
   * \brief Give implementations a chance to reject connections to their input
   * ports.
   *
   * If not reimplemented, all connections to ports within [0, inputCount()-1]
   * will be accepted.
   */
  virtual bool inputAcceptable(int port, const AbstractColumn* source) {
    Q_UNUSED(port);
    Q_UNUSED(source);
    return true;
  }
  /**
   * \brief Called whenever an input is disconnected or deleted.
   *
   * This is only to notify implementations of the event, the default
   * implementation is a
   * no-op.
   */
  virtual void inputAboutToBeDisconnected(const AbstractColumn* source) {
    Q_UNUSED(source);
  }

 protected slots:

  //!\name signal handlers
  //@{
  /**
   * \brief Name and/or comment of an input will be changed.
   *
   * \param source is always the this pointer of the column that emitted the
   * signal.
   */
  virtual void inputDescriptionAboutToChange(const AbstractColumn* source) {
    Q_UNUSED(source);
  }
  void inputDescriptionAboutToChange(const AbstractAspect* aspect) {
    const AbstractColumn* col = qobject_cast<const AbstractColumn*>(aspect);
    if (col) inputDescriptionAboutToChange(col);
  }
  //!
  /**
   * \brief Name and/or comment of an input changed.
   *
   * \param source is always the this pointer of the column that emitted the
   * signal.
   */
  virtual void inputDescriptionChanged(const AbstractColumn* source) {
    Q_UNUSED(source);
  }
  void inputDescriptionChanged(const AbstractAspect* aspect) {
    const AbstractColumn* col = qobject_cast<const AbstractColumn*>(aspect);
    if (col && d_inputs.contains(col)) inputDescriptionChanged(col);
  }
  /**
   * \brief The plot designation of an input is about to change.
   *
   * \param source is always the this pointer of the column that emitted the
   * signal.
   */
  virtual void inputPlotDesignationAboutToChange(const AbstractColumn* source) {
    Q_UNUSED(source);
  };
  /**
   * \brief The plot designation of an input changed.
   *
   * \param source is always the this pointer of the column that emitted the
   * signal.
   */
  virtual void inputPlotDesignationChanged(const AbstractColumn* source) {
    Q_UNUSED(source);
  }
  /**
   * \brief The display mode and possibly the data type of an input is about to
   * change.
   *
   * \param source is always the this pointer of the column that emitted the
   * signal.
   */
  virtual void inputModeAboutToChange(const AbstractColumn* source) {
    Q_UNUSED(source);
  }
  /**
   * \brief The display mode and possibly the data type has changed.
   *
   * \param source is always the this pointer of the column that emitted the
   * signal.
   */
  virtual void inputModeChanged(const AbstractColumn* source) {
    Q_UNUSED(source);
  }
  /**
   * \brief The data of an input is about to change.
   *
   * \param source is always the this pointer of the column that emitted the
   * signal.
   */
  virtual void inputDataAboutToChange(const AbstractColumn* source) {
    Q_UNUSED(source);
  }
  /**
   * \brief The data of an input has changed.
   *
   * \param source is always the this pointer of the column that emitted the
   * signal.
   */
  virtual void inputDataChanged(const AbstractColumn* source) {
    Q_UNUSED(source);
  }
  /**
   * \brief An input is about to be replaced.
   *
   * This signal is handled by AbstractFilter and mapped to
   * input(int,AbstractColumn*),
   * which calls inputDescriptionAboutToChange(),
   * inputPlotDesignationAboutToChange(),
   * inputDataAboutToChange(), inputMaskingAboutToChange(),
   * inputDescripionChanged(), inputMaskingChanged()
   * inputPlotDesignationChanged() and inputDataChanged().
   * inputModeAboutToChange() and inputModeChanged() are
   * called if the new column has a different mode (and thereby possibly data
   * type).
   * Thus, filter implementations won't have to bother with it most of the time.
   */
  virtual void inputAboutToBeReplaced(const AbstractColumn* source,
                                      const AbstractColumn* replacement);

  virtual void inputRowsAboutToBeInserted(const AbstractColumn* source,
                                          int before, int count) {
    Q_UNUSED(source);
    Q_UNUSED(before);
    Q_UNUSED(count);
  }
  virtual void inputRowsInserted(const AbstractColumn* source, int before,
                                 int count) {
    Q_UNUSED(source);
    Q_UNUSED(before);
    Q_UNUSED(count);
  }
  virtual void inputRowsAboutToBeRemoved(const AbstractColumn* source,
                                         int first, int count) {
    Q_UNUSED(source);
    Q_UNUSED(first);
    Q_UNUSED(count);
  }
  virtual void inputRowsRemoved(const AbstractColumn* source, int first,
                                int count) {
    Q_UNUSED(source);
    Q_UNUSED(first);
    Q_UNUSED(count);
  }
  virtual void inputMaskingAboutToChange(const AbstractColumn* source) {
    Q_UNUSED(source);
  }
  virtual void inputMaskingChanged(const AbstractColumn* source) {
    Q_UNUSED(source);
  }
  void inputAboutToBeDestroyed(const AbstractColumn* source) {
    input(portIndexOf(source), 0);
  }
  //@}

 protected:
  //! The data sources connected to my input ports.
  QVector<const AbstractColumn*> d_inputs;
};

#endif  // ABSTRACT_FILTER_H
